#include "trianglewidget.h"
#include "ui_trianglewidget.h"
#include <iostream>
#include <QApplication>
#include <QtOpenGLWidgets/QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QtOpenGL/QOpenGLTexture>
#include <QtOpenGL/QOpenGLShaderProgram>
#include <QtOpenGL/QOpenGLShader>
#include <QOpenGLTexture>

#include <opencv2/opencv.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <cmath>
#include <vector>

#include <QThread>
#include <QTimer>
#include <thread>

#include <QCoreApplication>

#define USE_VBO
using namespace std;
const char* vertexShaderSource = //顶点着色器
    "attribute vec3 pos;"
    "attribute vec2 a_texcoord;"
    "varying vec2 v_texcoord;"
    "void main()"//void main()函数是顶点着色器的入口，它定义了顶点着色器的主要计算逻辑，计算出每个顶点的最终位置，并将其输出到gl_Position中，以便后续的图形渲染过程使用。
    "{"
    "   gl_Position = vec4(pos.x, pos.y, pos.z, 1.0);"
    "   v_texcoord = a_texcoord;"
    "}";
const char* fragmentShaderSource = //片段着色器
    "uniform sampler2D texture;"
    "varying vec2 v_texcoord;"
    "void main()"
    "{"
    "   gl_FragColor = texture2D(texture, v_texcoord);"
    "}";
const char* vertexShaderSource2 = //顶点着色器
    "attribute vec2 a_line;"
    "void main()"
    "{"
    "   gl_Position = vec4(a_line.x,a_line.y,0.0, 1.0);"
    "}";
const char* fragmentShaderSource2 = //片段着色器
    "void main()"
    "{"
    "   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);"
    "}";
const char* vertexShaderSource3 = //顶点着色器
    "attribute vec2 a_line1;"
    "void main()"
    "{"
    "   gl_Position = vec4(a_line1.x,a_line1.y,0.0, 1.0);"
    "}";
const char* fragmentShaderSource3 = //片段着色器
    "void main()"
    "{"
    "   gl_FragColor = vec4(1.0,0.0,0.0,1.0);"
    "}";


GLfloat vertices2d[]={
    // 位置                 //纹理坐标
    1.0f, -1.0f, 0.0f, 1.0f, 0.0f,   // 右下
    -1.0f, -1.0f, 0.0f, 0.0f, 0.0f,   // 左下
    -1.0f,  1.0f, 0.0f, 0.0f, 1.0f,   // 左上
    1.0f,  1.0f, 0.0f, 1.0f, 1.0f,   // 右上
    1.0f, -1.0f, 0.0f, 1.0f, 0.0f,   //右下
    -1.0f,  1.0f, 0.0f, 0.0f, 1.0f   //左上
};

TriangleWidget::TriangleWidget(QOpenGLWidget *parent) ://是一个构造函数，用于创建 TriangleWidget 类的对象。这个类继承自 QOpenGLWidget，这意味着它是一个用于OpenGL渲染的Qt小部件。在构造函数中，它调用了 ui->setupUi(this)，可能是用于设置用户界面的初始化操作。
    QOpenGLWidget(parent),
    ui(new Ui::TriangleWidget)
{
    userValue = 150;
    m_UpdateFlag = true;
    ui->setupUi(this);
}
TriangleWidget::~TriangleWidget()//是类的析构函数，用于在对象被销毁时释放资源。在这里，它释放了 ui 对象的内存，以避免内存泄漏。
{
    delete ui;
}


void TriangleWidget::initializeGL()
{
    initializeOpenGLFunctions();//它通过initializeOpenGLFunctions确保OpenGL函数的正确加载
    programShader = new QOpenGLShaderProgram;//创建一个QOpenGLShaderProgram对象programShader
    programShader->create();
    programShader->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource);
    programShader->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource);
    //第二个着色器
    programShader2 = new QOpenGLShaderProgram;
    programShader2->create();
    programShader2->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource2);
    programShader2->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource2);
    //第三个着色器
    programShader3 = new QOpenGLShaderProgram;
    programShader3->create();
    programShader3->addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSource3);
    programShader3->addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSource3);
    //链接
    programShader->link();//并且通过addShaderFromSourceCode方法将顶点着色器和片段着色器源代码添加到该程序中。之后通过link方法将着色器链接到程序中。
    programShader2->link();
    programShader3->link();
    //
    texture = nullptr;//创建纹理
    //QImage image("/home/li/10.25/gl/image3.png");
    QImage image("/home/li/最新标定后的文件/undistortion_image.jpg");
    if (!image.isNull())
    {
        texture = new QOpenGLTexture(image.mirrored());
        texture->setMinificationFilter(QOpenGLTexture::Nearest);
        texture->setMagnificationFilter(QOpenGLTexture::Linear);
        texture->setWrapMode(QOpenGLTexture::Repeat);
    }


#ifdef USE_VBO
    vbo.create();
    vbo.bind();
    vbo.allocate(vertices2d, sizeof(vertices2d));//调用allocate()为VBO对象分配内存传递数据
    vbo.release();

    vbo1.create();

    vbo2.create();



#endif
}



void TriangleWidget::ChangeWidget()
{
    while (true)
    {
        if (!m_UpdateFlag)
        {
            update();
            m_UpdateFlag = true;
        }

        std::this_thread::sleep_for(std::chrono::seconds(1));

    }
}



void TriangleWidget::resizeGL(int w, int h)//resizeGL函数设置了OpenGL视口的大小，确保与窗口的大小匹配
{
    glViewport(0, 0, w, h);

}

void TriangleWidget::paintGL()
{

    cout<<userValue<<endl;

    if (m_UpdateFlag)
    {

//        m_UserValueMutex.lock();
        // 参数方程的参数
        double C = 2.83 / tan(userValue* CV_PI / 180) ;  // 圆心位置
        double Cl = 2.83 / tan(userValue* CV_PI / 180) +w/2;  //左后轮
        double A = sin(userValue * CV_PI / 180)  / 2.83;  // 角度转弧度
        double Cr = 2.83 / tan(userValue* CV_PI / 180) -w/2;//右后轮
//        m_UserValueMutex.unlock();
        // 绘制轨迹
        for (double t = 0; t <= 2 * CV_PI; t += 0.05) {
            double x = Cl * cos(A * t) - C;
            double y = Cl * sin(A * t) + 0.96;
            cv::Point2d point1;
            point1.x = x*1000+a;
            point1.y = y*1000+b;
            cv::Point2d Pt3d1;
            Pt3d1.x = point1.x;
            Pt3d1.y = point1.y; //将坐标原点转换
            cv::Mat mat_pt = (cv::Mat_<double>(4, 1) << Pt3d1.x, Pt3d1.y, 0, 1);
            cv::Mat mat_rst = MMatrix_back * mat_pt;
            cv::Point2d rst1;
            rst1.x = mat_rst.at<double>(0, 0) / mat_rst.at<double>(2, 0);
            rst1.y = mat_rst.at<double>(1, 0) / mat_rst.at<double>(2, 0);
            if(rst1.x<1280&&rst1.x>0&&rst1.y<720&&rst1.y>300)
            {
                cv::Point2d rst2;
                rst2.x = (rst1.x - 640)/640;
                rst2.y = (360 - rst1.y)/360;
                pointss.push_back(rst2);//将第一段轨迹线存储在该容器里
            }

        }


        for (double t = 0; t <= 2 * CV_PI; t += 0.01) {
            double x1 = Cr * cos(A * t) - C;
            double y1 = Cr * sin(A * t) + 0.96;
            cv::Point2d point2;
            point2.x = x1*1000+a;
            point2.y = y1*1000+b;
            cv::Point2d Pt3d2;
            Pt3d2.x = point2.x;
            Pt3d2.y = point2.y; //将坐标原点转换
            cv::Mat mat_pt = (cv::Mat_<double>(4, 1) << Pt3d2.x, Pt3d2.y, 0, 1);
            cv::Mat mat_rst = MMatrix_back * mat_pt;
            cv::Point2d rst3;
            rst3.x = mat_rst.at<double>(0, 0) / mat_rst.at<double>(2, 0);
            rst3.y = mat_rst.at<double>(1, 0) / mat_rst.at<double>(2, 0);
            if(rst3.x<1280&&rst3.x>0&&rst3.y<720&&rst3.y>300)
            {
                cv::Point2d rst4;
                rst4.x = (rst3.x - 640)/640;
                rst4.y = (360 - rst3.y)/360;
                pointsss.push_back(rst4);//将第二段轨迹线的点存储在这里
            }

        }


        programShader->bind();//首先通过programShader->bind()方法绑定着色器程序。
#ifdef USE_VBO //然后根据USE_VBO宏的定义分别执行不同的操作。如果定义了USE_VBO宏，它将绑定vbo缓冲区，
        vbo.bind();
        glClearColor(0.0, 1.0, 0.0, 1.0);
#else
        glClearColor(0.0, 0.0, 0.0, 1.0);
#endif
//    glClear(GL_COLOR_BUFFER_BIT);//并且设置清除颜色为(0.1, 0.5, 0.1, 1.0)。否则，设置清除颜色为(0.1, 0.1, 0.5, 1.0)。

#ifdef USE_VBO //然后根据USE_VBO宏的定义执行不同的操作。
        posAttr = programShader->attributeLocation("pos");//接着，通过attributeLocation方法获得顶点着色器中顶点位置属性的位置，并将其存储在posAttr变量中。
        texcoordLocation = programShader->attributeLocation("a_texcoord");  //获取纹理坐标属性的位置。
        quintptr offset = 0;
        programShader->setAttributeBuffer(posAttr, GL_FLOAT, offset, 3, sizeof(float) * 5);//如果定义了USE_VBO宏，它将通过setAttributeArray方法指定顶点属性数组，
        //1.数据传递到这一个顶点属性 2.数据类型 3.起始位置的偏移量(Offset) 4.指定顶点属性的大小 5.步长
        programShader->enableAttributeArray(posAttr);//并通过enableAttributeArray方法启用顶点属性数组。
        offset = sizeof(float) * 3;
        programShader->setAttributeBuffer(texcoordLocation, GL_FLOAT, offset, 2, sizeof(float) * 5);
        programShader->enableAttributeArray(texcoordLocation);
        texture->bind();                                    //将texture绑定到纹理单元0
        programShader->setUniformValue("texture", 0);      //让着色采样器ourTexture从纹理单元0中获取纹理数据
        GLuint id = vbo.bufferId();
        glEnableVertexAttribArray(id);

#else
        glVertexAttribPointer(posAttr, 3, GL_FLOAT, GL_FALSE, 0, vertices);
        glEnableVertexAttribArray(0);//否则，它将通过glVertexAttribPointer和glEnableVertexAttribArray函数指定顶点属性数据。
#endif
        glDrawArrays(GL_TRIANGLES, 0, 6);
#ifdef USE_VBO
        vbo.release();
#endif
        programShader->release();


//绘制第二个

#ifdef USE_VBO
        programShader2->bind();
        const std::vector<cv::Point2d>& data = pointss;
        // 假设 vbo1 的类型是 QVector2D，适用于 QVector2D 格式的数据
        QVector2D* pointArray = new QVector2D[data.size()];
        for (size_t i = 0; i < data.size(); ++i) {
            pointArray[i] = QVector2D(data[i].x, data[i].y);
        }
        //        vbo1.create();
        vbo1.bind();
        vbo1.allocate(pointArray, data.size() * sizeof(QVector2D));

#else
#endif
#ifdef USE_VBO
        linelocation = programShader2->attributeLocation("a_line");
        offset = 0;
        programShader2->setAttributeBuffer(linelocation, GL_FLOAT, offset, 2, sizeof(float) * 2);
        programShader2->enableAttributeArray(linelocation);
        GLuint id2 = vbo1.bufferId();
        glEnableVertexAttribArray(id2);
#else
#endif
        glDrawArrays(GL_LINE_STRIP, 0, pointss.size());

#ifdef USE_VBO
        vbo1.release();
        pointss.clear();
#endif
        programShader2->release();



        //绘制第三个
        programShader3->bind();
#ifdef USE_VBO
        const std::vector<cv::Point2d>& data2 = pointsss;
        QVector2D* pointArray2 = new QVector2D[data2.size()];
        for (size_t i = 0; i < data2.size(); ++i) {
            pointArray2[i] = QVector2D(data2[i].x, data2[i].y);
        }
        //        vbo2.create();
        vbo2.bind();
        vbo2.allocate(pointArray2, data2.size() * sizeof(QVector2D));

#else
#endif
#ifdef USE_VBO
        linelocation2 = programShader3->attributeLocation("a_line1");
        offset = 0;
        programShader3->setAttributeBuffer(linelocation2, GL_FLOAT, offset, 2, sizeof(float) * 2);
        programShader3->enableAttributeArray(linelocation2);
        GLuint id3 = vbo2.bufferId();
        glEnableVertexAttribArray(id3);
#else
#endif
        glDrawArrays(GL_LINE_STRIP, 0, pointsss.size());
#ifdef USE_VBO
        vbo2.release();
        pointsss.clear();
#endif
        programShader3->release();

        m_UpdateFlag = false;
        if (userValue == 213)
            userValue = 150;
        else
            userValue+=7;
    }
}
